--  --------------------------------------------------------------------------
--  @File:        list.adb
--
--  @Project:     Softwarequalitaet Blatt 4 / Aufgabe 7
--  @Version:     1.0
--  @Created:     2012-24-01
--  @Author:      Benedikt Markt, Aleksander Zotov
--
--  @Description: Linked list package;
--
--  @Modifications:


package body List is


   --  -----------------------------------------------------------------------
   --  @Procedure: Clear
   --
   --  @Description: Removes all elements from a list and frees all resources;
   --  @Parameter  : +List: list to clear;
   procedure Clear (List : in out List_Type) is
      Tmp : List_Element_Ref_Type;
   begin
      if List.Length > 0 then
         Tmp := List.Head;
         for I in 1 .. List.Length - 1 loop
            Tmp := Tmp.Next;
            Free (Tmp.Prev);
         end loop;
         Free (Tmp);
         List.Head := null;
         List.Tail := null;
         List.Length := 0;
      end if;
   end Clear;

   --  -----------------------------------------------------------------------
   --  @Procedure: Insert
   --
   --  @Description: Inserts an element in the list on a specified position;
   --  @Parameter  : +List: list to insert the element to;
   --                +Value: value of the element;
   --                +Index: the index;
   --                +Positioning: positioning type in reference to the index;
   procedure Insert (List        : in out List_Type;
                     Value       : in Value_Type;
                     Index       : in Natural;
                     Positioning : in Positioning_Type) is
      Tmp : List_Element_Ref_Type;

   begin
      if Index = 0 then
         raise Index_Out_Of_Bounds;
      end if;
      Tmp := List.Head;
      case Positioning is
         when Head => Append (List, Value);
         when Tail => Prepend (List, Value);
         when Before =>
            for I in 1 .. Index - 1 loop
               if Tmp.Next = null then
                  raise Index_Out_Of_Bounds;
               else
                  Tmp := Tmp.Next;
               end if;
            end loop;
            if Tmp.Prev /= null then
               Tmp.Prev.Next := new List_Element_Type'(Value, Tmp.Prev, Tmp);
               Tmp.Prev := Tmp.Prev.Next;
               List.Length := List.Length + 1;
            else
               Prepend (List, Value);
            end if;
         when After =>
            for I in 1 .. Index - 1 loop
               if Tmp.Next = null then
                  raise Index_Out_Of_Bounds;
               else
                  Tmp := Tmp.Next;
               end if;
            end loop;
            if Tmp.Next /= null then
               Tmp.Next := new List_Element_Type'(Value, Tmp, Tmp.Next);
               Tmp.Next.Next.Prev := Tmp.Next;
               List.Length := List.Length + 1;
            else
               Append (List, Value);
            end if;
      end case;
   end Insert;

   --  -----------------------------------------------------------------------
   --  @Procedure: Append
   --
   --  @Description: Inserts an element at the end of the list;
   --  @Parameter  : +List: list to insert the element to;
   --                +Value: value of the element;
   procedure Append (List : in out List_Type;
                     Value : in Value_Type) is
   begin
      if List.Length = 0 then
         List.Head := new List_Element_Type'(Value, null, null);
         List.Tail := List.Head;
         List.Length := 1;
      else
         List.Tail.Next := new List_Element_Type'(Value, List.Tail, null);
         List.Tail := List.Tail.Next;
         List.Length := List.Length + 1;
      end if;
   end Append;

   --  -----------------------------------------------------------------------
   --  @Procedure: Prepend
   --
   --  @Description: Inserts an element in beginning of the list;
   --  @Parameter  : +List: list to insert the element to;
   --                +Value: value of the element;
   procedure Prepend (List : in out List_Type;
                      Value : in Value_Type) is
   begin
      if List.Length = 0 then
         List.Head := new List_Element_Type'(Value, null, null);
         List.Tail := List.Head;
         List.Length := 1;
      else
         List.Head.Prev := new List_Element_Type'(Value, null, List.Head);
         List.Head := List.Head.Prev;
         List.Length := List.Length + 1;
      end if;
   end Prepend;

   --  -----------------------------------------------------------------------
   --  @Function: Value
   --
   --  @Description: Returns the value of an element at given index;
   --  @Parameter  : +List: list in which the element is located;
   --                +Index: index of the element;
   --  @Return     : Value of the element;
   function Value (List : in List_Type;
                   Index : in Natural) return Value_Type is
      Tmp : List_Element_Ref_Type;
   begin
      if Index = 0 then
         raise Index_Out_Of_Bounds;
      end if;
      Tmp := List.Head;
      for I in 1 .. Index - 1 loop
         if Tmp.Next = null then
            raise Index_Out_Of_Bounds;
         else
            Tmp := Tmp.Next;
         end if;
      end loop;
      return Tmp.Value;
   end Value;

   --  -----------------------------------------------------------------------
   --  @Procedure: Remove
   --
   --  @Description: Deletes an element from a specific location;
   --  @Parameter  : +List: list from which the element will be deleted;
   --                +Index: index of the element;
   procedure Remove (List : in out List_Type;
                     Index : in Natural) is
      Tmp : List_Element_Ref_Type;
   begin
      if Index = 0 then
         raise Index_Out_Of_Bounds;
      end if;
      if List.Length = 0 then
         raise Index_Out_Of_Bounds;
      end if;
      Tmp := List.Head;
      for I in 1 .. Index - 1 loop
         if Tmp.Next = null then
            raise Index_Out_Of_Bounds;
         else
            Tmp := Tmp.Next;
         end if;
      end loop;
      if List.Length = 1 then
         List.Head := null;
         List.Tail := null;
      elsif Tmp.Next = null then
         Tmp.Prev.Next := null;
         List.Tail := Tmp.Prev;
      elsif Tmp.Prev = null then
         Tmp.Next.Prev := null;
         List.Head := Tmp.Next;
      else
         Tmp.Next.Prev := Tmp.Prev;
         Tmp.Prev.Next := Tmp.Next;
      end if;
      Free (Tmp);
      List.Length := List.Length - 1;
   end Remove;

   --  -----------------------------------------------------------------------
   --  @Function: Length
   --
   --  @Description: Returns the length of a list;
   --  @Parameter  : +List: the list;
   --  @Return     : Length of the list
   function Length (List : in List_Type) return Natural is
   begin
      return List.Length;
   end Length;
end List;
